open Ast

module FunctionMap = Map.Make(String)
let fmap = FunctionMap.empty
let fmap = FunctionMap.add "print_int" 1 fmap
let fmap = FunctionMap.add "print_str" 2 fmap

module NameMap = Map.Make (struct
 type t = string
 let compare x y = Pervasives.compare x y
end)
let funcmap = NameMap.empty

module TypeMap = Map.Make(String)
let tmap = TypeMap.empty
let tmap = TypeMap.add "int" 1 tmap
let tmap = TypeMap.add "string" 2 tmap

module IdMap = Map.Make(String)
let imap = IdMap.empty
let imap = IdMap.add "Hello" 2 imap
let imap = IdMap.add "hi" 3 imap

module IntMap = Map.Make(String)
let intmap = IntMap.empty

module StringMap = Map.Make(String)
let strmap = StringMap.empty

module IntarrMap = Map.Make(String)
let intarrmap = IntarrMap.empty

let intArray = Array.make 50 0
let strArray = Array.make 50 ""
let array_int = Array.make 30 intArray;;

let stringTemp = ""
let int_num =ref 0
let str_num =ref 0
let intarr_num =ref 0
let func_num = ref 3
let addone = fun num -> num+1
(*let intAdd = fun id int_num ->if (IntMap.mem id intmap)then( raise (Failure("redeclared variable")))
                        else(let intmap = IntMap.add id int_num intmap in intmap)*)
(*let intAdd id int_num = if (IntMap.mem id intmap)then( raise (Failure("redeclared variable")))
                        else(let intmap=IntMap.add id int_num intmap )*)

let assignment = fun id expr -> if (IntMap.mem id intmap)then(intArray.(IntMap.find id intmap) <- expr;expr )  
                                else raise (Failure("undeclared int variable"))          

let execList l f = List.hd (List.rev (List.map f l) )
(*let execList l f m = List.fold_left f m l*)
let print_int = fun s ->let a = print_endline(string_of_int (List.hd s)) in List.hd s
let print_str = fun s ->let a = print_endline strArray.(List.hd s) in 11
                   
let add = fun s ->let a =print_endline(string_of_int( List.hd s + List.hd (List.tl s))) in 2

let callnew = fun index varible -> match index with
    1 -> print_int varible
|   2 -> print_str varible

let getbody funclist = List.map ( fun func->func.body ) funclist 
(*looking for int variable*)
let varint intmap stmtl =
let intAdd = fun intmap id int_num ->if (IntMap.mem id intmap)then( raise (Failure("redeclared variable")))
                        else(let intmap = IntMap.add id int_num intmap in intmap )
in
let rec exprid intmap = function
  Int(id) -> 
    ((ignore (int_num:=!int_num+1);int_num.contents), (intAdd intmap id int_num.contents))
 | _ -> (0,intmap)
in
 	let rec stmtid intmap= function
      	    Expr(e) ->
        	exprid intmap e
	  | Block(b) ->
                (1,(List.fold_left (fun intmap stmt -> snd(stmtid intmap stmt) ) intmap b))
          | _ -> (0,intmap)
in stmtid intmap stmtl
(*looking for int variable*)

let sndvarint intmap stmtl = snd(varint intmap stmtl)

let vint intmap prog=
let stmtlist = getbody prog 
in
let a = List.fold_left (fun intmap stmtl -> sndvarint intmap stmtl) intmap stmtlist 
in a

(*string*)
let varstr strmap stmt =
let strAdd = fun strmap id str_num ->if (StringMap.mem id strmap)then( raise (Failure("redeclared variable")))
                        else(let strmap = StringMap.add id str_num strmap in strmap )
in
let rec exprid strmap = function
  Stringv(id) -> 
    ((ignore (str_num:=!str_num+1);str_num.contents), (strAdd strmap id str_num.contents))
 | _ -> (0,strmap)
in
 	let rec stmtid strmap= function
      	    Expr(e) ->
        	exprid strmap e
	  | Block(b) ->
                (1,(List.fold_left (fun strmap stmt -> snd(stmtid strmap stmt) ) strmap b))
          | _ -> (0,strmap)
in stmtid strmap stmt
(*string*)
let sndvarstr strmap stmtl = snd(varstr strmap stmtl)

let vstr intmap prog=
let stmtlist = getbody prog 
in
let a = List.fold_left (fun strmap stmtl -> sndvarstr strmap stmtl) strmap stmtlist 
in a

(*int array*)
let intarr intarrmap stmt =
let intarrAdd = fun intarrmap id intarr_num ->if (IntarrMap.mem id intarrmap)then( raise (Failure("redeclared variable")))
                        else(let intarrmap = StringMap.add id intarr_num intarrmap in intarrmap )
in
let rec exprid intarrmap = function
  IntArray(id,n) -> 
    ((ignore (intarr_num:=!intarr_num+1);intarr_num.contents), ((array_int.(intarr_num.contents)<-(Array.make n 0));intarrAdd intarrmap id intarr_num.contents))
 | _ -> (0,intarrmap)
in
 	let rec stmtid intarrmap= function
      	    Expr(e) ->
        	exprid intarrmap e
	  | Block(b) ->
                (1,(List.fold_left (fun intarrmap stmt -> snd(stmtid intarrmap stmt) ) intarrmap b))
          | _ -> (0,intarrmap)
in stmtid intarrmap stmt
(*int array*)

let sndintarr intarrmap stmtl = snd(intarr intarrmap stmtl)

let iarr intarrmap prog=
let stmtlist = getbody prog 
in
let a = List.fold_left (fun intarrmap stmtl -> sndintarr intarrmap stmtl) intarrmap stmtlist 
in a

let funcdecl prog =
   let funcmap = List.fold_left
       (fun funcs fdecl -> NameMap.add fdecl.fname fdecl funcs)
       funcmap prog
   in funcmap




let traslate stringTemp intmap strmap intarrmap funcmap prog =

let rec call statement=
let rec expr = function 
    Id(s) -> (((if(IntMap.mem s intmap) then intArray.(IntMap.find s intmap) 
                else if(StringMap.mem s strmap) then (IntMap.find s strmap)
                else raise (Failure("undeclared int variable")))),stringTemp)
  | Int (id) ->
      (0,stringTemp)
      (*((ignore (int_num:=!int_num+1);int_num.contents), (intAdd id int_num.contents))*)
  | Stringv(id) ->
      (1,stringTemp)
  | IntArray(id,n) -> 
      (2,stringTemp)
  | Char (c) -> ((int_of_char c),stringTemp)
  | Str (s) -> (1,(String.sub s 1 ((String.length s)-2)))
 (* | Call (fname, actuals) -> 
      ((let v1 = FunctionMap.find fname fmap and v2 =List.map (fun a -> fst(expr a)) actuals in 
      call v1 v2),stringTemp)*)
  | Call (fname, actuals) ->
      if(NameMap.mem fname funcmap) then call(NameMap.find fname funcmap).body
      else raise (Failure("function is not defined")) 
      (*print_endline (fname);FunctionMap.find fname fmap*)
  (*| Seq (expr1, expr2) ->
      ignore(expr expr1); expr expr2*)
  | Lit(x) -> (x,stringTemp)  
  | Assign(id,e) -> if (IntMap.mem id intmap)then((intArray.(IntMap.find id intmap) <- (fst(expr e));(fst(expr e))),stringTemp )  
                    else if (StringMap.mem id strmap) then ((strArray.(StringMap.find id strmap) <- (snd(expr e));(fst(expr e))),snd(expr e) ) 
                                else raise (Failure("undeclared int variable")) 
  | ArrAssign(id,n,e) -> if (IntarrMap.mem id intarrmap) then (((array_int.(IntarrMap.find id intarrmap).(n) <- fst(expr e));fst(expr e)),snd (expr e))
                                else raise (Failure("undeclared int variable")) 
  | Binop(e1, op, e2) ->
      ((let v1 = fst(expr e1) and v2 = fst(expr e2) in
      match op with
	Add -> v1 + v2
      | Sub -> v1 - v2
      | Mul -> v1 * v2
      | Div -> v1 / v2),stringTemp)
in 
	let rec stmt= function
      	    Expr(e) ->
        	expr  e 
	  | Block(b) ->
                 execList b stmt

in stmt  statement
in 
   if (NameMap.mem "main" funcmap) then call(NameMap.find "main" funcmap).body
   else raise (Failure("cannot find the main function")) 

(*let _ =
let lexbuf = Lexing.from_channel stdin in
  let program = Parser.stmt Scanner.token lexbuf in
  let intmap = snd(traslate intmap program) in intmap*)

let _ =
  let lexbuf = Lexing.from_channel stdin in
  let program = Parser.program Scanner.token lexbuf in
  (*let intmap = snd(varint intmap program) in*)
  let intmap = vint intmap program in
  (*let strmap = snd(varstr strmap program) in*)
  let strmap = vstr strmap program in
  (*let intarrmap = snd(intarr intarrmap program) in*)
  let intarrmap = iarr intarrmap program in
  let funcmap = funcdecl program in
  let result = traslate stringTemp intmap strmap intarrmap funcmap program  in
  print_endline ( string_of_int(fst(result)));"test"


